summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt341
1 files changed, 139 insertions, 202 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 16323a316..b4117d761 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -28,12 +28,9 @@ import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationBarView
-import kotlinx.coroutines.CoroutineScope
import java.io.File
import java.io.FilenameFilter
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -43,7 +40,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
-import org.yuzu.yuzu_emu.getPublicFilesDir
+import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.TaskState
@@ -60,15 +57,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val homeViewModel: HomeViewModel by viewModels()
private val gamesViewModel: GamesViewModel by viewModels()
private val taskViewModel: TaskViewModel by viewModels()
+ private val addonViewModel: AddonViewModel by viewModels()
override var themeId: Int = 0
- private val savesFolder
- get() = "${getPublicFilesDir().canonicalPath}/nand/user/save/0000000000000000"
-
- // Get first subfolder in saves folder (should be the user folder)
- val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
-
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -145,6 +137,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
}
}
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ homeViewModel.contentToInstall.collect {
+ if (it != null) {
+ installContent(it)
+ homeViewModel.setContentToInstall(null)
+ }
+ }
+ }
+ }
}
// Dismiss previous notifications (should not happen unless a crash occurred)
@@ -253,13 +255,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
super.onResume()
}
- override fun onStop() {
- super.onStop()
- CoroutineScope(Dispatchers.IO).launch {
- NativeConfig.saveSettings()
- }
- }
-
override fun onDestroy() {
EmulationActivity.stopForegroundService(this)
super.onDestroy()
@@ -468,110 +463,150 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> ->
- if (documents.isNotEmpty()) {
- IndeterminateProgressDialogFragment.newInstance(
- this@MainActivity,
- R.string.installing_game_content
- ) {
- var installSuccess = 0
- var installOverwrite = 0
- var errorBaseGame = 0
- var errorExtension = 0
- var errorOther = 0
- documents.forEach {
- when (
- NativeLibrary.installFileToNand(
- it.toString(),
- FileUtil.getExtension(it)
- )
- ) {
- NativeLibrary.InstallFileToNandResult.Success -> {
- installSuccess += 1
- }
+ if (documents.isEmpty()) {
+ return@registerForActivityResult
+ }
- NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
- installOverwrite += 1
- }
+ if (addonViewModel.game == null) {
+ installContent(documents)
+ return@registerForActivityResult
+ }
- NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
- errorBaseGame += 1
- }
+ IndeterminateProgressDialogFragment.newInstance(
+ this@MainActivity,
+ R.string.verifying_content,
+ false
+ ) {
+ var updatesMatchProgram = true
+ for (document in documents) {
+ val valid = NativeLibrary.doesUpdateMatchProgram(
+ addonViewModel.game!!.programId,
+ document.toString()
+ )
+ if (!valid) {
+ updatesMatchProgram = false
+ break
+ }
+ }
- NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
- errorExtension += 1
- }
+ if (updatesMatchProgram) {
+ homeViewModel.setContentToInstall(documents)
+ } else {
+ MessageDialogFragment.newInstance(
+ this@MainActivity,
+ titleId = R.string.content_install_notice,
+ descriptionId = R.string.content_install_notice_description,
+ positiveAction = { homeViewModel.setContentToInstall(documents) }
+ )
+ }
+ }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
+ }
- else -> {
- errorOther += 1
- }
+ private fun installContent(documents: List<Uri>) {
+ IndeterminateProgressDialogFragment.newInstance(
+ this@MainActivity,
+ R.string.installing_game_content
+ ) {
+ var installSuccess = 0
+ var installOverwrite = 0
+ var errorBaseGame = 0
+ var errorExtension = 0
+ var errorOther = 0
+ documents.forEach {
+ when (
+ NativeLibrary.installFileToNand(
+ it.toString(),
+ FileUtil.getExtension(it)
+ )
+ ) {
+ NativeLibrary.InstallFileToNandResult.Success -> {
+ installSuccess += 1
+ }
+
+ NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
+ installOverwrite += 1
+ }
+
+ NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
+ errorBaseGame += 1
+ }
+
+ NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
+ errorExtension += 1
+ }
+
+ else -> {
+ errorOther += 1
}
}
+ }
- val separator = System.getProperty("line.separator") ?: "\n"
- val installResult = StringBuilder()
- if (installSuccess > 0) {
- installResult.append(
- getString(
- R.string.install_game_content_success_install,
- installSuccess
- )
+ addonViewModel.refreshAddons()
+
+ val separator = System.getProperty("line.separator") ?: "\n"
+ val installResult = StringBuilder()
+ if (installSuccess > 0) {
+ installResult.append(
+ getString(
+ R.string.install_game_content_success_install,
+ installSuccess
)
+ )
+ installResult.append(separator)
+ }
+ if (installOverwrite > 0) {
+ installResult.append(
+ getString(
+ R.string.install_game_content_success_overwrite,
+ installOverwrite
+ )
+ )
+ installResult.append(separator)
+ }
+ val errorTotal: Int = errorBaseGame + errorExtension + errorOther
+ if (errorTotal > 0) {
+ installResult.append(separator)
+ installResult.append(
+ getString(
+ R.string.install_game_content_failed_count,
+ errorTotal
+ )
+ )
+ installResult.append(separator)
+ if (errorBaseGame > 0) {
installResult.append(separator)
- }
- if (installOverwrite > 0) {
installResult.append(
- getString(
- R.string.install_game_content_success_overwrite,
- installOverwrite
- )
+ getString(R.string.install_game_content_failure_base)
)
installResult.append(separator)
}
- val errorTotal: Int = errorBaseGame + errorExtension + errorOther
- if (errorTotal > 0) {
+ if (errorExtension > 0) {
installResult.append(separator)
installResult.append(
- getString(
- R.string.install_game_content_failed_count,
- errorTotal
- )
+ getString(R.string.install_game_content_failure_file_extension)
)
installResult.append(separator)
- if (errorBaseGame > 0) {
- installResult.append(separator)
- installResult.append(
- getString(R.string.install_game_content_failure_base)
- )
- installResult.append(separator)
- }
- if (errorExtension > 0) {
- installResult.append(separator)
- installResult.append(
- getString(R.string.install_game_content_failure_file_extension)
- )
- installResult.append(separator)
- }
- if (errorOther > 0) {
- installResult.append(
- getString(R.string.install_game_content_failure_description)
- )
- installResult.append(separator)
- }
- return@newInstance MessageDialogFragment.newInstance(
- this,
- titleId = R.string.install_game_content_failure,
- descriptionString = installResult.toString().trim(),
- helpLinkId = R.string.install_game_content_help_link
- )
- } else {
- return@newInstance MessageDialogFragment.newInstance(
- this,
- titleId = R.string.install_game_content_success,
- descriptionString = installResult.toString().trim()
+ }
+ if (errorOther > 0) {
+ installResult.append(
+ getString(R.string.install_game_content_failure_description)
)
+ installResult.append(separator)
}
- }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
- }
+ return@newInstance MessageDialogFragment.newInstance(
+ this,
+ titleId = R.string.install_game_content_failure,
+ descriptionString = installResult.toString().trim(),
+ helpLinkId = R.string.install_game_content_help_link
+ )
+ } else {
+ return@newInstance MessageDialogFragment.newInstance(
+ this,
+ titleId = R.string.install_game_content_success,
+ descriptionString = installResult.toString().trim()
+ )
+ }
+ }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
val exportUserData = registerForActivityResult(
@@ -632,7 +667,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
// Clear existing user data
- NativeConfig.unloadConfig()
+ NativeConfig.unloadGlobalConfig()
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
// Copy archive to internal storage
@@ -651,108 +686,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Reinitialize relevant data
NativeLibrary.initializeSystem(true)
- NativeConfig.initializeConfig()
+ NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
-
- /**
- * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
- */
- val exportSaves = registerForActivityResult(
- ActivityResultContracts.CreateDocument("application/zip")
- ) { result ->
- if (result == null) {
- return@registerForActivityResult
- }
-
- IndeterminateProgressDialogFragment.newInstance(
- this,
- R.string.save_files_exporting,
- false
- ) {
- val zipResult = FileUtil.zipFromInternalStorage(
- File(savesFolderRoot),
- savesFolderRoot,
- BufferedOutputStream(contentResolver.openOutputStream(result))
- )
- return@newInstance when (zipResult) {
- TaskState.Completed -> getString(R.string.export_success)
- TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
- }
- }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
- }
-
- private val startForResultExportSave =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { _ ->
- File(getPublicFilesDir().canonicalPath, "temp").deleteRecursively()
- }
-
- val importSaves =
- registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null) {
- return@registerForActivityResult
- }
-
- NativeLibrary.initializeEmptyUserDirectory()
-
- val inputZip = contentResolver.openInputStream(result)
- // A zip needs to have at least one subfolder named after a TitleId in order to be considered valid.
- var validZip = false
- val savesFolder = File(savesFolderRoot)
- val cacheSaveDir = File("${applicationContext.cacheDir.path}/saves/")
- cacheSaveDir.mkdir()
-
- if (inputZip == null) {
- Toast.makeText(
- applicationContext,
- getString(R.string.fatal_error),
- Toast.LENGTH_LONG
- ).show()
- return@registerForActivityResult
- }
-
- val filterTitleId =
- FilenameFilter { _, dirName -> dirName.matches(Regex("^0100[\\dA-Fa-f]{12}$")) }
-
- try {
- CoroutineScope(Dispatchers.IO).launch {
- FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
- cacheSaveDir.list(filterTitleId)?.forEach { savePath ->
- File(savesFolder, savePath).deleteRecursively()
- File(cacheSaveDir, savePath).copyRecursively(
- File(savesFolder, savePath),
- true
- )
- validZip = true
- }
-
- withContext(Dispatchers.Main) {
- if (!validZip) {
- MessageDialogFragment.newInstance(
- this@MainActivity,
- titleId = R.string.save_file_invalid_zip_structure,
- descriptionId = R.string.save_file_invalid_zip_structure_description
- ).show(supportFragmentManager, MessageDialogFragment.TAG)
- return@withContext
- }
- Toast.makeText(
- applicationContext,
- getString(R.string.save_file_imported_success),
- Toast.LENGTH_LONG
- ).show()
- }
-
- cacheSaveDir.deleteRecursively()
- }
- } catch (e: Exception) {
- Toast.makeText(
- applicationContext,
- getString(R.string.fatal_error),
- Toast.LENGTH_LONG
- ).show()
- }
- }
}